#include "../crypt/crypt.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libnet.h>
#include <time.h>
#include "../utils/utils.h"

char* requete;

char* setPayload(char* ip){
  
  printf("Construction du paquet...");

  time_t t;
  struct tm *temps; 
  
  /* Récupération du timestamp */
  time(&t); 
  temps = localtime(&t); 
  //printf ("\nTimestamp : %dh%dmin:%ds\n", temps->tm_hour, temps->tm_min, temps->tm_sec);
  
  /* Récupération du temps en chaîne de caractère */
  char* timeChar;
  if(asprintf(&timeChar, "%ld", t) == -1)
    printf ("Erreur lors de la conversion du timestamp en chaine!!!\n");
  
  /* Récupération d'un nombre aléatoire */
  srand((unsigned)time(NULL));  
  int random = rand();
  char* randomCharValue;
  if(asprintf(&randomCharValue, "%d", random) == -1)
    printf ("Erreur lors de la conversion du nombre améatoire en chaine!!!\n");
  
  /* On forme la chaine à hasher */
  char* payloadWithoutHash;
  if(asprintf(&payloadWithoutHash, "%s%c%s%c%s%c%s%c", ip, '|', timeChar, '|', randomCharValue, '|', requete, '|') == -1)
    printf ("Erreur lors de la concatenation des paramètressans le hash!!!\n");

  /* On calcule le hash */
  int tailleHash;
  char * hashPayload = hash(MHASH_MD5, payloadWithoutHash, &tailleHash);
  char* hashPayloadb64;
  hashPayloadb64 = (char *) malloc(((tailleHash/3)*4+8) * sizeof(char));
  b64_encode(hashPayload, hashPayloadb64, tailleHash);

  /* On forme le payload finale */
  char* payload;
  int taillePayload;
  if((taillePayload = asprintf(&payload, "%s%s",payloadWithoutHash, hashPayloadb64)) == -1)
    printf ("Erreur lors de la concatenation des paramètres et du hash!!!\n");

  /* On libère la mémoire */
  free(timeChar);
  free(randomCharValue);
  free(payloadWithoutHash);
  free(hashPayload);
  free(hashPayloadb64);


  printf("OK\n");
  return payload;
}

void readIpSource(char* ip_addr_str_src, libnet_t* l, u_int32_t* ip_addr_src){

  u_int8_t *ip_addr_p_src;
  
  /* IP address source*/
  /*printf("@IP source: ");
    scanf("%15s",ip_addr_str_src);*/

  *ip_addr_src = libnet_name2addr4(l, ip_addr_str_src,LIBNET_DONT_RESOLVE);

  if (*ip_addr_src != -1 ) {
    ip_addr_p_src = (u_int8_t*)(ip_addr_src);
    //printf("Address read: %d.%d.%d.%d\n", ip_addr_p_src[0],ip_addr_p_src[1], ip_addr_p_src[2], ip_addr_p_src[3]);
  }
  else
    fprintf(stderr, "Error converting @IP Source.\n");
}

void readIpDest(char* ip_addr_str_dest, libnet_t* l, u_int32_t* ip_addr_dest){
   u_int8_t *ip_addr_p_dest;

  /* IP address destination*/
  /*printf("@IP destination: ");
    scanf("%15s",ip_addr_str_dest);*/

  *ip_addr_dest = libnet_name2addr4(l, ip_addr_str_dest, LIBNET_DONT_RESOLVE);

  if (*ip_addr_dest != -1 ) {
    ip_addr_p_dest = (u_int8_t*)(ip_addr_dest);
    //printf("Address read: %d.%d.%d.%d\n", ip_addr_p_dest[0],ip_addr_p_dest[1], ip_addr_p_dest[2], ip_addr_p_dest[3]);
  }
  else
    fprintf(stderr, "Error converting @IP Destination.\n"); 
  
}

int setPacket(libnet_t* l, char *cypherPayload, int tailleCypher, u_int32_t ip_addr_src, u_int32_t ip_addr_dest, int src_port, int dst_port){ 
 
  libnet_ptag_t udp = libnet_build_udp(
				       src_port,  /* source port */
				       dst_port,  /* destination port */
				       LIBNET_UDP_H + tailleCypher,  /* packet length */
				       0,  /* checksum */
				       cypherPayload,  /* payload */
				       tailleCypher,  /* payload size */
				       l,  /* libnet handle */
				       0);  /* libnet id */
  
  if (udp == -1)
    {
      fprintf(stderr, "Can't build UDP header: %s\n", libnet_geterror(l));
      goto bad;
    }

  libnet_ptag_t ip = libnet_build_ipv4(
				       LIBNET_IPV4_H + LIBNET_UDP_H + tailleCypher,  /* length */
				       0,  /* TOS */
				       242,  /* IP ID */
				       0,  /* IP Frag */
				       64,  /* TTL */
				       IPPROTO_UDP,  /* protocol */
				       0,  /* checksum */
				       ip_addr_src,  /* source IP */
				       ip_addr_dest,  /* destination IP */
				       NULL,  /* payload */
				       0,  /* payload size */
				       l,  /* libnet handle */
				       0);  /* libnet id */
  if (ip == -1)
    {
      fprintf(stderr, "Can't build IP header: %s\n", libnet_geterror(l));
      exit(EXIT_FAILURE);
    }
  
  printf("Envoi du paquet : ");
  fflush(stdout);
  int c = libnet_write(l);
  if (c == -1)
    {
      fprintf(stderr, "Erreur d'ecriture: %s\n", libnet_geterror(l));
      goto bad;
    }
  else
    {
      fprintf(stderr, "Ecriture d'un paquet de %d octet%c.\n\n", c, c > 1 ? 's' : ' ');
    }
  
  
  
  return (EXIT_SUCCESS);
 bad:
 
  return (EXIT_FAILURE); 
}

char* cryptage(char* payload, int* tailleCypher){

  printf("Cryptage du payload...");
  fflush(stdout);
  char algo[] = MCRYPT_RIJNDAEL_256;
  char mode[] = MCRYPT_CBC;
  MCRYPT td;
   
  init_module(&td, algo, mode);
  int tailleIV = mcrypt_enc_get_block_size(td) + 1;
  char iv[tailleIV];
  char* passwordIV = "ae12(kDJeu14d5fUH54-ik$fhxsd=1234ds5ze4dTDC42";
   
  char* key = readKey();
  

  key_generator(iv, tailleIV, passwordIV);
  iv[tailleIV] = '\0';
  /*memset(iv, 0, tailleIV);
  strcpy(iv, "clef");*/
    /* Initialisation du cryptage*/
  if(mcrypt_generic_init(td, key, strlen(key), iv) < 0)
  	fprintf(stderr, "Erreur lors de l'initialisation du cryptage...\n");
  
  /* Cryptage du message */
  char* cypher = crypt_block(&td, payload, tailleCypher);
  
  close_module(&td);
 
  
  /* codage du cypher et de l'iv en base 64 */
  char* cypher64;
  cypher64 = (char *) malloc((((*tailleCypher)/3)*4+8) * sizeof(char));
  int tailleCypher64 = b64_encode(cypher, cypher64, *tailleCypher);

  char* iv64;
  iv64 = (char *) malloc(((tailleIV/3)*4+8) * sizeof(char));
  int tailleIV64 = b64_encode(iv, iv64, tailleIV);
	
 /* Récupération du temps en chaîne de caractère */
  char* cypherAndIV;
  if((*tailleCypher=asprintf(&cypherAndIV, "%s%c%s", cypher64, '=', iv64)) == -1)
    printf ("Erreur lors de la concatenation de l'IV et du cypher!!!\n");

  //printf("Payload à envoyé: %s\n\n", cypherAndIV);

  free(iv64);
  free(cypher64);
  free(cypher);
  //free(key);
  printf("OK\n");
  return cypherAndIV;
}

int main(int argc, char *argv[]){
  requete = "";
  libnet_t *l;  /* the libnet context */
  char errbuf[LIBNET_ERRBUF_SIZE]; /* Buffer d'erreur */
  
  char *ip_addr_str_src, *ip_addr_str_dest;
  int src_port = 10000, dest_port = 10000;
  u_int32_t ip_addr_src, ip_addr_dest;  
  
  if(argc < 4){
    printf("Usage: %s <ip source> <ip dest> <liste_services>\n", argv[0]);
    return 0;
  }
  else{
	printf("Lecture des informations fournies...\n");
    ip_addr_str_src = argv[1];
    ip_addr_str_dest = argv[2];
    requete = argv[3];
  }
  
  /*------------------------------------------------------------------------------
  * HANDLE LIBNET
  * -----------------------------------------------------------------------------*/
  /* On initialise libnet. en lui spécifiant qu'on va se charger de forger l'en-tête IP+UDP (-> Libnet_raw4); */
  l = libnet_init(LIBNET_RAW4, NULL, errbuf);
  if ( l == NULL ) {
    fprintf(stderr, "libnet_init() failed: %s\n", errbuf);
    exit(EXIT_FAILURE);
  }

  
  /*------------------------------------------------------------------------------
  * Lecture adresse IP
  * -----------------------------------------------------------------------------*/
  
  /* IP address source*/
  readIpSource(ip_addr_str_src, l, &ip_addr_src);
  
  /* IP address destination*/
  readIpDest(ip_addr_str_dest, l, &ip_addr_dest);


  /*------------------------------------------------------------------------------
  * Construction du payload
  * -----------------------------------------------------------------------------*/

  /* Construction du payload */
  char* payload = setPayload(ip_addr_str_src);

  
  /*------------------------------------------------------------------------------
  * MCRYPT
  * -----------------------------------------------------------------------------*/
  
  /* Cryptage du message */
  int taillePayload64;
  char *payloadb64 = cryptage(payload, &taillePayload64);

  /* codage du payload crypté en base 64 */
  /*char* payloadb64;
  payloadb64 = (char *) malloc(((tailleCypher/3)*4+8) * sizeof(char));
  int taillePayload64 = b64_encode(cypherPayload, payloadb64, tailleCypher);
  printf("Codage en base 64 du payload crypté terminé: %s\n\n", payloadb64);*/

   /*------------------------------------------------------------------------------
  * Envoi packet
  * -----------------------------------------------------------------------------*/

  /* Construction et envoi du packet */
  int success = setPacket(l, payloadb64, taillePayload64, ip_addr_src, ip_addr_dest, src_port, dest_port);
  printf("mise en veille durant 3 secondes avant rejeu\n");
  sleep(3);
  int c = libnet_write(l);
  if (c == -1)
      fprintf(stderr, "Write error: %s\n", libnet_geterror(l));
  printf("packet rejoué\n");
  printf("mise en veille durant 40 secondes avant rejeu\n");
  sleep(40);
  c = libnet_write(l);
  if (c == -1)
      fprintf(stderr, "Write error: %s\n", libnet_geterror(l));
  printf("packet rejoué\n");
  /* Libère l'espace */

  /* Libère l'espace */
  libnet_destroy(l);
  free(payload);
  free(payloadb64);

  return success;
}
